{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h1>Create React App Structure Using Multi Agents </h1>\n",
    "<h3>Use OpenAI and deepseek to create an app structure for React app. </h3>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Let's import environment variables\n",
    "from dotenv import load_dotenv\n",
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import json\n",
    "from typing import Dict, Any\n",
    "from IPython.display import Markdown, display\n",
    "from openai import OpenAI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
    "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
    "\n",
    "if not openai_api_key:\n",
    "    print('Missing OpenaAI API key.')\n",
    "if not deepseek_api_key:\n",
    "    print('Missing Deepseek API key')\n",
    "if openai_api_key and deepseek_api_key:\n",
    "    print(f'OpenAI: {openai_api_key[-10:]}\\n')\n",
    "    print(f'Deepseek: {deepseek_api_key[-10:]}\\n')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "app = {\"app_name\": \"Small Business Idea\"}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "openai = OpenAI()\n",
    "deepseek = OpenAI(api_key=deepseek_api_key, \n",
    "    base_url=\"https://api.deepseek.com\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# system prompt and user prompt  \n",
    " \n",
    "system_prompt = \"\"\"\n",
    "You're an entrepreneur focused on developing and investing in \n",
    "emerging AI-driven SaaS applications that solve critical pain\n",
    "points for small businesses—such as bookkeeping, reservations,\n",
    "tax preparation, and employee records management. \n",
    "\n",
    "You prioritize solutions leveraging agentic AI to address \n",
    "real-world business challenges with minimal human oversight,\n",
    "delivering both scalability and innovation. Your goal is to \n",
    "identify ideas with the highest potential for market disruption\n",
    "while helping small businesses save time and money.\n",
    "\n",
    "List all the business areas that might be worth exploring for \n",
    "Agentic AI.\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "user_prompt = \"List all the business area that might be worth exploring for Agentic AI.\"\n",
    "\n",
    "messages = [\n",
    "    {\"role\": \"system\", \"content\":system_prompt},\n",
    "    {\"role\": \"user\", \"content\": user_prompt},\n",
    "]\n",
    "\n",
    "# Call openai\n",
    "response = deepseek.chat.completions.create(\n",
    "    model=\"deepseek-chat\",\n",
    "    messages=messages\n",
    ")\n",
    "\n",
    "business_ideas = response.choices[0].message.content\n",
    "display(Markdown(business_ideas))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Best idea prompt\n",
    "selected_idea_prompt = f\"\"\"Select the best idea from the list: {business_ideas} areas. \n",
    "Give reasons and why this pain point is the best to solve.\n",
    "List only the top idea.\"\"\"\n",
    "\n",
    "second_messages = [\n",
    "    {\"role\": \"system\", \"content\": system_prompt},\n",
    "    {\"role\": \"user\", \"content\": selected_idea_prompt}\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call openai to select the best idea \n",
    "response = openai.chat.completions.create(\n",
    "    messages=second_messages,\n",
    "    model=\"gpt-4.1-mini\"\n",
    ")\n",
    "\n",
    "selected_idea = response.choices[0].message.content\n",
    "display(Markdown(selected_idea))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add idea and pain points \n",
    "app['idea'] = selected_idea"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Let's create an app structure for the selected idea \n",
    "# Break the f-string into smaller parts for better readability and to avoid nesting issues\n",
    "system_prompt = \"Please create a react app file directory structure. You're given the business idea, along with the following pain points.\"\n",
    "structure_prompt = \"\"\"\n",
    "Respond in clear JSON format only, remove any backticks, extra spaces. The structure should also include \n",
    "frontend pages, authentication, api, stripe payment, and a backend database along with\n",
    "any necessary directories and files for the app to work without any errors.\n",
    "Respond with JSON format with name of the file, and path where the file should be stored, for example:\n",
    "\n",
    "{\n",
    "  \"root\": {\n",
    "    \"public\": {\n",
    "      \"index.html\": \"root/public/index.html\",\n",
    "      \"css\": {\n",
    "        \"style.css\": \"root/public/css/style.css\"\n",
    "      },\n",
    "      \"images\": {\n",
    "        \"logo.png\": \"root/public/images/logo.png\"\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "}\n",
    "\"\"\"\n",
    "\n",
    "create_structure_prompt = f\"{system_prompt}\\n{structure_prompt}\"\n",
    "\n",
    "structure_prompt= [\n",
    "    {\"role\": \"system\", \"content\": system_prompt},\n",
    "    {\"role\": \"user\", \"content\": create_structure_prompt}\n",
    "]\n",
    "\n",
    "response  = openai.chat.completions.create(\n",
    "    messages=structure_prompt,\n",
    "    model=\"gpt-4.1-mini\"    \n",
    ")\n",
    "structure = response.choices[0].message.content\n",
    "display(Markdown(structure))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "app[\"app_structure\"] =  structure"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "structure_check_prompt = f\"\"\"You're a an expert react app developer. You validate \n",
    "react app file structure for the idea \n",
    "{selected_idea}\\n.\n",
    "If there're any errors with the structure, for example if there're missing files, directories, or any extra \n",
    "modifications needed to make the structure better, please respond \n",
    "with 'Needs modification' text/word only. \n",
    "\n",
    "If the structure doesn't need modification, simply \n",
    "respond with 'Correct structure' text/word only.\n",
    "\"\"\"\n",
    "\n",
    "structure_check= [\n",
    "    {\"role\": \"system\", \"content\": system_prompt},\n",
    "    {\"role\": \"user\", \"content\": structure_check_prompt}\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "We need to double check if the app structure is correct. We can use other models, \n",
    "deepseek seems to add extra files, and stays out of context, so let's stick with \n",
    "openai for now. \n",
    "\"\"\"\n",
    "response = deepseek.chat.completions.create(\n",
    "    messages=structure_check,\n",
    "    model=\"deepseek-chat\"  \n",
    ")\n",
    "\n",
    "double_check = response.choices[0].message.content\n",
    "display(Markdown(double_check))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Check if the file structure is correct \n",
    "correct_structure = (double_check == 'Correct structure')\n",
    "\n",
    "if not correct_structure: # Only try if structure is incorrect \n",
    "    print(f\"Structure needs correction: {double_check}\")\n",
    "    max_count = 0\n",
    "    updated_structure = structure # Start with the original \n",
    "    \n",
    "    while max_count < 3 and not correct_structure:\n",
    "        \n",
    "        content = f\"\"\"Please correct the file structure {structure} for the selected idea \n",
    "        {selected_idea}. Respond with clear JSON format only, with no backticks.\"\"\"\n",
    "        json_format = f\"\"\"Please follow this example JSON structure:\n",
    "        If the structure is correct please respond with only 'Correct structure' text only.\"\"\"\n",
    "        example =\"\"\"\n",
    "        {\n",
    "            \"root\": {\n",
    "                \"public\": {\n",
    "                \"index.html\": \"root/public/index.html\",\n",
    "                \"css\": {\n",
    "                    \"style.css\": \"root/public/css/style.css\"\n",
    "                },\n",
    "                \"images\": {\n",
    "                    \"logo.png\": \"root/public/images/logo.png\"\n",
    "                }\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "        \"\"\"\n",
    "        \n",
    "        retry_message = f\"{content}\\n {selected_idea}\\n{json_format}\\n{example}\"\n",
    "        \n",
    "        response = openai.chat.completions.create(\n",
    "        messages=[\n",
    "            {\"role\":\"system\", \"content\": system_prompt},\n",
    "            {\"role\": \"user\",\"content\": f\"{retry_message}\"}\n",
    "        ],\n",
    "        model=\"gpt-4.1-mini\"\n",
    "        )\n",
    "        \n",
    "        response = response.choices[0].message.content\n",
    "        \n",
    "        if response == 'Correct structure':\n",
    "            correct_structure = True\n",
    "            print(\"Structure is already correct, no modification needed.\")\n",
    "            \n",
    "        else:\n",
    "            # Retry\n",
    "            updated_structure = response \n",
    "            max_count += 1 \n",
    "            print(f\">>> Retrying...{max_count}\")\n",
    "    \n",
    "    # Update the app structure with the last/corrected version\n",
    "    app['app_structure'] = json.loads(updated_structure )\n",
    "    \n",
    "else:\n",
    "    print(\"Structure is already correct\")\n",
    "    app[\"app_structure\"] = json.loads(structure)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "app['app_structure']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Save as JSON file \n",
    "with open('app_structure.json', 'w') as f:\n",
    "    json.dump(app['app_structure'],f, indent=4)\n",
    "    \n",
    "    print(\"App structure saved to app_structure.json\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create the file structure recursively, from structure in current directory\n",
    "def create_file_structure(structure: Dict, parent_dir:str='.'):\n",
    "    \"\"\"Create file structure recursively from structure. \"\"\"\n",
    "    try:\n",
    "        for file, folder in structure.items():\n",
    "            path = os.path.join(parent_dir, file)\n",
    "            if isinstance(folder, dict):\n",
    "                # It's a directory\n",
    "                os.makedirs(path, exist_ok=True)\n",
    "                create_file_structure(folder, path) # recursively create the sub folder structure\n",
    "            else:\n",
    "                # It's a file, create empty file\n",
    "                os.makedirs(parent_dir, exist_ok=True)\n",
    "                \n",
    "                # Check file extension\n",
    "                valid_extensions = ('.ts', '.tsx', '.md', '.js', '.css', '.json', '.jsx', '.html', '.txt', '.db', '.py', '.sql')\n",
    "                \n",
    "                if file.endswith(valid_extensions):\n",
    "                    with open(path, 'w') as f:\n",
    "                        pass  # Create an empty file\n",
    "                else:\n",
    "                    print(f'Unknown file type {file}')\n",
    "\n",
    "    except Exception as e:\n",
    "        print(f\"Error creating file structure: {e}\")\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Open the app_structure file \n",
    "filepath = os.path.join(os.getcwd(),'app_structure.json')\n",
    "\n",
    "with open(filepath, 'r', encoding='utf-8') as f:\n",
    "    app_structure = json.load(f) \n",
    "\n",
    "create_file_structure(app_structure, parent_dir='./app/')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "system_prompt = f\"\"\"You're Senior react developer with over 10 years of experience. \n",
    "\"\"\"\n",
    "user_prompt = f\"\"\"You're given the following app details in the {app['app_structure']}\\n\n",
    "for the {selected_idea}. Please write the following files . \n",
    "\n",
    "\"package.json\": \"root/package.json\"\n",
    "\"README.md\": \"root/README.md\"\n",
    "\".gitignore\": \"root/.gitignore\"\n",
    "\"webpack.config.js\": \"root/webpack.config.js\"\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = [\n",
    "    {\"role\":\"system\", \"content\": system_prompt},\n",
    "    {\"role\": \"user\", \"content\": user_prompt}\n",
    "]\n",
    "\n",
    "response = openai.chat.completions.create(\n",
    "    messages=messages,\n",
    "    model=\"gpt-4.1-mini\"\n",
    ")\n",
    "\n",
    "source_response = response.choices[0].message.content\n",
    "display(Markdown(source_response))\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
